#ifndef ZQL_MATRIX
#define ZQL_MATRIX

#include <iostream>
#include <cstdlib>

using namespace std;

template <typename T>
class Matrix;

template <typename T>
class RowVector;

template <typename T>
class ColVector;

template <typename T>
Matrix<T> operator*(const Matrix<T>& _A, const Matrix<T>& _B);

template <typename T>
T operator*(const RowVector<T>& _x, const RowVector<T>& _y);

template <typename T>
T operator*(const ColVector<T>& _x, const ColVector<T>& _y);

template <typename T>
T operator*(const RowVector<T>& _x, const ColVector<T>& _y);

/* template <typename T> */
/* Matrix<T>& operator*(const ColVector<T>& _x, const RowVector<T>& _y); */


template <typename T>
class Matrix
{
protected:
    T* matrix_elements;
    int n_row = 0;
    int n_col = 0;
    void allocMem(int _n);
    void releaseMem();
    void copyMem(const T* _f, T* _t, int _n);
    
public:
    Matrix()//构造函数
    {
	matrix_elements = NULL;
    };
    Matrix(int _r, int _c);//构造函数
    Matrix(const Matrix<T>& _A);//复制构造函数
    Matrix<T>& operator = (const Matrix<T>& _A);//=运算符重载
    T& operator()(int _i, int _j);//()运算符重载
    ~Matrix();
    bool is_square(const Matrix<T>& _A);
    void warning(int _err);

    friend Matrix<T> operator*<>(const Matrix<T>& _A, const Matrix<T>& _B);
    /* friend Matrix<T>& operator*<>(const ColVector<T>& _x, const RowVector<T>& _y); */
};

template <typename T>
void Matrix<T>::allocMem(int _n)
{
    matrix_elements = new T [_n];
}

template <typename T>
void Matrix<T>::releaseMem()
{
    if (matrix_elements != NULL)
	delete [] matrix_elements;
    matrix_elements = NULL;
}

template <typename T>
void Matrix<T>::copyMem(const T* _f, T* _t, int _n)
{
    for(int i = 0; i < _n; i++)
	_t[i] = _f[i];
}

template <typename T>
void Matrix<T>::warning(int _err)
{
    switch(_err)
    {
    case 1:
	cerr << "index out of range!" << endl;
	releaseMem();
	exit(-1);
	break;
    case 2:
	cerr << "indexs of row and colomun of these two Matrix are not match!" << endl;
	releaseMem();
	exit(-1);
	break;
    default:
	break;
    }
}

template <typename T>
Matrix<T>::Matrix(int _r, int _c)
{
    n_row = _r;
    n_col = _c;
    allocMem(_r * _c);
}

template <typename T>
Matrix<T>::Matrix(const Matrix<T>& _A)
{
    int _r = _A.n_row;//方便使用
    int _c = _A.n_col;
    allocMem(_r * _c);
    copyMem(_A.matrix_elements, matrix_elements, _r * _c);
    n_row = _r;
    n_col = _c;
}

template <typename T>
Matrix<T>& Matrix<T>::operator=(const Matrix<T>& _A)
{
    int _r = _A.n_row;
    int _c = _A.n_col;
    releaseMem();
    allocMem(_r * _c);
    copyMem(_A.matrix_elements, matrix_elements, _r * _c);
    n_row = _r;
    n_col = _c;
    return *this;
}

template <typename T>
T& Matrix<T>::operator()(int _i, int _j)
{
    if (_i < 0 || _i > n_row - 1 || _j < 0 || _j > n_col - 1)
        warning(1);
    return *(matrix_elements + _i * n_col + _j);
}
    
template <typename T>
Matrix<T>::~Matrix()
{
    releaseMem();
}

template <typename T>
bool Matrix<T>::is_square(const Matrix<T>& _A)
{
    if (_A.n_row == _A.n_col)
    {
        cout << "It is a square matrix!" << endl;
        return true;
    }
    else
    {
        cout << "It is not a square matrix!" << endl;
        return false;
    }
}


template <typename T>
Matrix<T> operator*(const Matrix<T>& _A, const Matrix<T>& _B)
{
    int a = _A.n_row;
    int b = _A.n_col;
    int c = _B.n_row;
    int d = _B.n_col;
    Matrix<T> temp(a, d);
    if (b != c)
        temp.warning(2);
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < d; j++)
        {
            T sum = 0;
            for (int k = 0; k < b; k++)
	        sum += _A.matrix_elements[i * b + k] * _B.matrix_elements[k * d + j];
            temp.matrix_elements[i * d + j] = sum;
        }
    }
    return temp;//返回不加&，否则报错，也可改temp为statics类，但输出只能使用一次即销毁，故不推荐
}

/** 
 * RowVector
 * 
 * @param _c 
 * 
 * @return 
 */

template <typename T>
class RowVector : public Matrix<T>
{
public:
    RowVector(int _c):Matrix<T>(1,_c){};
    T& operator()(const int _i);
    friend T operator*<>(const RowVector<T>& _x, const RowVector<T>& _y);
    friend T operator*<>(const RowVector<T>& _x, const ColVector<T>& _y);
    /* friend Matrix<T>& operator*<>(const ColVector<T>& _x, const RowVector<T>& _y); */
};

template <typename T>
T& RowVector<T>::operator()(const int _i)
{
    if (_i < 0 || _i > this->n_col - 1)
	Matrix<T>::warning(1);
    return *(Matrix<T>::matrix_elements + _i);
}

template <typename T>
T operator*(const RowVector<T>& _x, const RowVector<T>& _y)
{
    int a = _x.n_col;
    int b = _y.n_col;
    Matrix<T> t(1,1);
    T temp = 0;
    if (a != b)
        t.Matrix<T>::warning(2);
    for (int i = 0; i < a; i++)
        temp += _x.matrix_elements[i] * _y.matrix_elements[i];
    return temp; 	
}


/** 
 * ColVector
 * 
 * 
 * @return 
 */

template <typename T>
class ColVector : public Matrix<T>
{
public:
    ColVector(int _r):Matrix<T>(_r,1){};
    T& operator()(const int _i);
    friend T operator*<>(const ColVector<T>& _x, const ColVector<T>& _y);
    friend T operator*<>(const RowVector<T>& _x, const ColVector<T>& _y);
    /* friend Matrix<T>& operator*<>(const ColVector<T>& _x, const RowVector<T>& _y); */
};

template <typename T>
T& ColVector<T>::operator()(const int _i)
{
    if (_i < 0 || _i > this->n_row - 1)
	Matrix<T>::warning(1);
    return *(Matrix<T>::matrix_elements + _i);
}

template <typename T>
T operator*(const ColVector<T>& _x, const ColVector<T>& _y)
{
    int a = _x.n_row;
    int b = _y.n_row;
    Matrix<T> t(1,1);
    T temp = 0;
    if (a != b)
        t.Matrix<T>::warning(2);
    for (int i = 0; i < a; i++)
        temp += _x.matrix_elements[i] * _y.matrix_elements[i];
    return temp; 	
}

template <typename T>
T operator*(const RowVector<T>& _x, const ColVector<T>& _y)
{
    int a = _x.n_col;
    int b = _y.n_row;
    Matrix<T> t(1,1);
    T temp = 0;
    if (a != b)
        t.Matrix<T>::warning(2);
    for (int i = 0; i < a; i++)
        temp += _x.matrix_elements[i] * _y.matrix_elements[i];
    return temp; 	
}

/* template <typename T> */
/* Matrix<T>& operator*(const ColVector<T>& _x, const RowVector<T>& _y) */
/* { */
/*     Matrix<T> A(_x); */
/*     Matrix<T> B(_y); */
/*     return  A * B; */
/* } */

#else
//donothing
#endif
